import React, { useCallback, useMemo, useState } from "react";
import { useValidation } from "./Context";
import { Validator } from "./Validator";

interface IValidatedFormProps<InitData={}> extends Validation.IValidatedFormContext {
    children: React.ReactNode;
    onSubmit?: (data: InitData) => void;
    onFaild?: (data: Validator[]) => void;
}

interface IExFormApi<INITData={}> {
    mount?: boolean;
    setValue: <Name extends keyof INITData>( name: Name, value: INITData[Name]) => void;
    getValue: <Name extends keyof INITData>( name: Name ) => INITData[Name];
    onValidated: (uid: { id: string, tag?: string, group?: string }, callback: (errorRule?: Validator) => void) => Function;
}

const ValidatedFormContext = React.createContext<Validation.IValidatedFormContext>({
    validateOnChange: true,
    tag: ""
});



export const ValidatedForm = (props: IValidatedFormProps) => {
    const validateApi = useValidation();
    const vConfig = useMemo(() => {
        const configData: any = {};
        Object.keys(props).forEach((key: string) => {
            if(!["children"].includes(key)) {
                configData[key] = (props as any)[key];
            }
        });
        return configData;
    }, [props]);
    const [ formData ] = useState<any>({
        data: {},
        events: []
    });
    const formContext = useMemo(() => ({
        ...vConfig,
        mount: true,
        setValue: (name: string, value: any) => {
            formData.data[name] = value;
        },
        getValue: (name: string) => formData.data[name],
        onValidated: ({ id, tag, group}: any, fn: Function) => {
            formData.events.push({
                id,
                tag,
                group,
                callback: fn
            });
            return () => {
                const index = formData.events.findIndex((item: any) => {
                    return item.id === id && item.tag === tag && item.group === group;
                });
                if(index > -1) {
                    formData.events.splice(index, 1);
                }
            };
        }
    }), [vConfig]);
    const onValidatedFormSubmit = useCallback((event: React.FormEvent<HTMLFormElement>) => {
        event.preventDefault();
        event.stopPropagation();
        let validateResult: Validator[]  = [];
        if(props.tag && props.group) {
            validateResult = validateApi.validateByTagAndGroup(props.tag, props.group, {
                value: formData.data
            });
        } else if(props.tag && !props.group) {
            validateResult = validateApi.validateByTag(props.tag, {
                value: formData.data
            });
        }
        formData.events.forEach((item: any) => {
            const findValidator = validateResult.find((v) => {
                return v.option.validatedId === item.id && v.option.validatedTag === item.tag && v.option.validatedGroup === item.group;
            });
            item.callback(findValidator);
        });
        if(validateResult.length > 0) {
            typeof props.onFaild === "function" && props.onFaild(validateResult);
        } else {
            typeof props.onSubmit === "function" && props.onSubmit(formData.data);
        }
        return false;
    }, []);
    return (<ValidatedFormContext.Provider value={formContext}>
        <form onSubmit={onValidatedFormSubmit}>
            {props.children}
        </form>
    </ValidatedFormContext.Provider>);
};

export const useValidatedForm = <Data={}>(): Validation.IValidatedFormContext & IExFormApi<Data> => {
    const context = React.useContext(ValidatedFormContext);
    if (!context) {
        throw new Error("useValidatedForm must be used within a ValidatedForm");
    }
    return context as any;
};